#include <stdlib.h>
#include <boost/lexical_cast.hpp>
#include <vector>

#include "include/Scene.h"
#include "include/GlobalGameObj.h"
#include "include/Utils.h"

using namespace boost;

int Scene::tolerance_distence = 5;
int Scene::total_tolerance = 50;

string Scene::GetSceneObjKey(string scene_id, int type, int id)
{
    return "sceneObj_" + scene_id + lexical_cast<string>(type) +
        "_" + lexical_cast<string>(id);
}

void Scene::AddSceneObj(GlobalGameObj *p_game_obj, SceneObj *p_scene_obj)
{
    string key = GetSceneObjKey(p_scene_obj->scene_id, p_scene_obj->obj_type,
                                p_scene_obj->obj_id);

	string value;
	SceneObj2Str(p_scene_obj, value);
	if(p_game_obj->model_proxy.SetMemcacheObject(key, value))
	{
		p_game_obj->error_code = Common::ModelError;
		p_game_obj->error_str = "replace memcache object error for add scene obj";
	}
}

void Scene::GetSceneObj(GlobalGameObj *p_game_obj, SceneObj *p_scene_obj)
{
    string key = GetSceneObjKey(p_scene_obj->scene_id, p_scene_obj->obj_type,
                                p_scene_obj->obj_id);

    string value;
    if(p_game_obj->model_proxy.GetMemcacheObject(key, value))
    {
        p_game_obj->error_code = Common::ModelError;
        p_game_obj->error_str = "get memcache object error for get scene obj";

        return;
    }

    Str2SceneObj(value, p_scene_obj);
}

void Scene::GetSceneObjTree(GlobalGameObj *p_game_obj, SceneObj *p_scene_obj, ptree &scene_obj_tree)
{
	scene_obj_tree.clear();

	GetSceneObj(p_game_obj, p_scene_obj);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

	scene_obj_tree.put("characterId", p_scene_obj->obj_id);
	scene_obj_tree.put("resId", p_scene_obj->obj_res_id);
	scene_obj_tree.put("x", p_scene_obj->x);
	scene_obj_tree.put("y", p_scene_obj->y);
	scene_obj_tree.put("isMoving", p_scene_obj->is_moving);
	scene_obj_tree.put("endX", p_scene_obj->end_x);
	scene_obj_tree.put("endY", p_scene_obj->end_y);
	scene_obj_tree.put("arriveTime", p_scene_obj->arrive_time);
}

int Scene::GetEntryPos(string &pos_str, int &x, int &y)
{
	int i;
	vector<string> str_list;
	Utils::Str2StrList(pos_str, str_list, '^');

	if(0 == str_list.size())
	{
		return 1;
	}

	if(1 == str_list.size())
	{
		i = 0;
	}
	else
	{
		i = rand() % str_list.size();
	}

	pos_str = str_list[i];
	Utils::Str2StrList(pos_str, str_list, '_');

	if(0 == str_list.size())
	{
		return 1;
	}

	x = atoi(str_list[0].c_str());
	y = atoi(str_list[1].c_str());

	return 0;
}

void Scene::GetScenePlayer(GlobalGameObj *p_game_obj, string scene_id,
	vector<int> &player_list, vector<int> &character_list)
{
	string value;
	string key = "scenePlayer_" + scene_id;
	string sql = "select `player_id`, `character_id` from tbl_character where"
		"`scene_id` like \"" + scene_id + "\" and `online` = 1;";

	player_list.clear();
	character_list.clear();

	if(p_game_obj->model_proxy.GetObjectBySql(key, sql, value))
	{
	    p_game_obj->error_code = Common::ModelError;
		p_game_obj->error_str = "Scene::GetScenePlayer";

		return;
	}

    cout << "scene player value: " << value << endl;

	vector<string> str_list;
	Utils::Str2StrList(value, str_list, '|');

	for(unsigned int i = 0; i < str_list.size(); i++)
	{
		int player_id;
		int character_id;

		sscanf(str_list[i].c_str(), "%i_%i", &player_id, &character_id);

		player_list.push_back(player_id);
		character_list.push_back(character_id);
	}
}

void Scene::UpdateScenePlayer(GlobalGameObj *p_game_obj, string scene_id)
{
	if(p_game_obj->model_proxy.DelMemcacheObject("scenePlayer" + scene_id))
	{
		p_game_obj->error_code = Common::ModelError;
		p_game_obj->error_str = "Scene::UpdateScenePlayer";
	}
}

void Scene::GetSceneInfo(GlobalGameObj *p_game_obj, string scene_id, SceneInfo *p_info)
{
	string value;

    cout << "scene_id: " << scene_id << endl;
	if(p_game_obj->model_proxy.GetObjectById("tbl_scene", "scene_id", scene_id, value))
	{
		p_game_obj->error_code = Common::ModelError;
		p_game_obj->error_str = "Scene::GetSceneInfo";

		return;
	}

	vector<string> strList;
	Utils::Str2StrList(value, strList, '|');

	int i = 0;
	p_info->scene_id = strList[i++].c_str();
	p_info->scene_type = atoi(strList[i++].c_str());
	p_info->scene_name = strList[i++].c_str();
	p_info->res_id = atoi(strList[i++].c_str());
	p_info->max_x = atoi(strList[i++].c_str());
	p_info->max_y = atoi(strList[i++].c_str());
	p_info->entry_str = strList[i++].c_str();
	p_info->exit_str = strList[i++].c_str();
	p_info->npc_str = strList[i++].c_str();
	p_info->monster_str = strList[i++].c_str();
}

void Scene::GetSceneInfoTree(GlobalGameObj *p_game_obj, string scene_id, ptree &scene_tree)
{
	SceneInfo scene_info;

	scene_tree.clear();

	GetSceneInfo(p_game_obj, scene_id, &scene_info);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

	scene_tree.put("sceneId", scene_info.scene_id);
	scene_tree.put("sceneType", scene_info.scene_type);
	scene_tree.put("sceneName", scene_info.scene_name);
	scene_tree.put("sceneResId", scene_info.res_id);

	// exit info

	vector<ExitInfo *> exit_info_list;
	GetExitInfo(scene_info.exit_str, exit_info_list);

	if(exit_info_list.size() > 0)
	{
		ptree exit_tree;

		for(unsigned int i = 0; i < exit_info_list.size(); i++)
		{
			ptree tree_item;
			tree_item.put("exitId", exit_info_list[i]->exit_id);
			tree_item.put("x", exit_info_list[i]->x);
			tree_item.put("y", exit_info_list[i]->y);
			tree_item.put("exitName", exit_info_list[i]->exit_name);
			tree_item.put("resId", exit_info_list[i]->res_id);
			tree_item.put("destSceneId", exit_info_list[i]->dst_scene_id);

			exit_tree.push_back(make_pair("", tree_item));

			// release exit info, important!
			delete exit_info_list[i];
			exit_info_list[i] = NULL;
		}

		scene_tree.put_child("exit", exit_tree);
	}

	// npc info

	vector<Scene::NpcInfo *> npc_info_list;
	GetNpcInfo(scene_info.npc_str, npc_info_list);

	if(npc_info_list.size() > 0)
	{
		ptree npc_tree;

		for(unsigned int i = 0; i < npc_info_list.size(); i++)
		{
			ptree tree_item;
			tree_item.put("npcId", npc_info_list[i]->npc_id);
			tree_item.put("x", npc_info_list[i]->x);
			tree_item.put("y", npc_info_list[i]->y);
			tree_item.put("npcName", npc_info_list[i]->npc_name);
			tree_item.put("resId", npc_info_list[i]->res_id);

			npc_tree.push_back(make_pair("", tree_item));

			// release npc info, important!
			delete npc_info_list[i];
			npc_info_list[i] = 0;
		}

		scene_tree.put_child("npc", npc_tree);
	}

	// monster info
	vector<Scene::MonsterInfo *> monster_info_list;
	GetMonsterInfo(scene_info.monster_str, monster_info_list);

	if(monster_info_list.size() > 0)
	{
		ptree monster_tree;

		for(unsigned int i = 0; i < monster_info_list.size(); i++)
		{
			ptree tree_item;
			tree_item.put("monsterId", monster_info_list[i]->monster_id);
			tree_item.put("x", monster_info_list[i]->x);
			tree_item.put("y", monster_info_list[i]->y);
			tree_item.put("monsterName", monster_info_list[i]->monster_name);
			tree_item.put("resId", monster_info_list[i]->res_id);

			monster_tree.push_back(make_pair("", tree_item));

			// release monster info, important!
			delete monster_info_list[i];
			monster_info_list[i] = 0;
		}

		scene_tree.put_child("monster", monster_tree);
	}
}

void Scene::GetExitInfo(string &exit_str, vector<ExitInfo *> & exit_info_list)
{
	exit_info_list.clear();

	vector<string> exit_str_list;
	Utils::Str2StrList(exit_str, exit_str_list, '^');
	if(0 == exit_info_list.size())
	{
		return;
	}

	vector<string> exit_info_str_list;
	for(unsigned int i = 0; i < exit_info_list.size(); i++)
	{
		Utils::Str2StrList(exit_str_list[i], exit_info_str_list, '_');
		if(6 != exit_info_str_list.size())
		{
			continue;
		}

		ExitInfo *p_exit_info = new ExitInfo();
		p_exit_info->exit_id = atoi(exit_info_str_list[0].c_str());
		p_exit_info->x = atoi(exit_info_str_list[1].c_str());
		p_exit_info->y = atoi(exit_info_str_list[2].c_str());
		p_exit_info->exit_name = exit_info_str_list[3].c_str();
		p_exit_info->res_id = atoi(exit_info_str_list[4].c_str());
		p_exit_info->dst_scene_id = atoi(exit_info_str_list[5].c_str());

		exit_info_list.push_back(p_exit_info);
	}
}

void Scene::GetNpcInfo(string &npc_str, vector<NpcInfo *> & npc_info_list)
{
	npc_info_list.clear();

	vector<string> npc_str_list;
	Utils::Str2StrList(npc_str, npc_str_list, '^');
	if(0 == npc_info_list.size())
	{
		return;
	}

	vector<string> npc_info_str_list;
	for(unsigned int i = 0; i < npc_info_list.size(); i++)
	{
		Utils::Str2StrList(npc_str_list[i], npc_info_str_list, '_');
		if(5 != npc_info_str_list.size())
		{
			continue;
		}

		NpcInfo *p_npc_info = new NpcInfo();
		p_npc_info->npc_id = atoi(npc_info_str_list[0].c_str());
		p_npc_info->x = atoi(npc_info_str_list[1].c_str());
		p_npc_info->y = atoi(npc_info_str_list[2].c_str());
		p_npc_info->npc_name = npc_info_str_list[3];
		p_npc_info->res_id = atoi(npc_info_str_list[4].c_str());

		npc_info_list.push_back(p_npc_info);
	}
}

void Scene::GetMonsterInfo(string &monster_str, vector<MonsterInfo *> & monster_info_list)
{
	monster_info_list.clear();

	vector<string> monster_str_list;
	Utils::Str2StrList(monster_str, monster_str_list, '^');
	if(0 == monster_info_list.size())
	{
		return;
	}

	vector<string> monster_info_str_list;
	for(unsigned int i = 0; i < monster_info_list.size(); i++)
	{
		Utils::Str2StrList(monster_str_list[i], monster_info_str_list, '_');
		if(5 != monster_info_str_list.size())
		{
			continue;
		}

		MonsterInfo *p_monster_info = new MonsterInfo();
		p_monster_info->monster_id = atoi(monster_info_str_list[0].c_str());
		p_monster_info->x = atoi(monster_info_str_list[1].c_str());
		p_monster_info->y = atoi(monster_info_str_list[2].c_str());
		p_monster_info->monster_name = monster_info_str_list[3];
		p_monster_info->res_id = atoi(monster_info_str_list[4].c_str());

		monster_info_list.push_back(p_monster_info);
	}
}

void Scene::SceneObj2Str(SceneObj *p_scene_obj, string &value)
{
	value = p_scene_obj->scene_id + "|" + lexical_cast<string>(p_scene_obj->obj_type) +
		"|" + lexical_cast<string>(p_scene_obj->obj_id) + "|" +
		lexical_cast<string>(p_scene_obj->obj_res_id) + "|" +
		lexical_cast<string>(p_scene_obj->scene_type) + "|" +
		lexical_cast<string>(p_scene_obj->speed) + "|" +
		lexical_cast<string>(p_scene_obj->x) + "|" +
		lexical_cast<string>(p_scene_obj->y) + "|" +
		lexical_cast<string>(p_scene_obj->is_moving) + "|" +
		p_scene_obj->last_time + "|" + lexical_cast<string>(p_scene_obj->start_x) + "|" +
		lexical_cast<string>(p_scene_obj->start_y) + "|" +
		lexical_cast<string>(p_scene_obj->end_x) + "|" +
		lexical_cast<string>(p_scene_obj->end_y) + "|" + p_scene_obj->arrive_time + "|" +
		lexical_cast<string>(p_scene_obj->move_tolerance);
}

void Scene::Str2SceneObj(string &value, SceneObj*p_scene_obj)
{
    cout << __LINE__ << " in " << __FUNCTION__ << endl;

    vector<string> str_list;
    Utils::Str2StrList(value, str_list, '|');
    if(16 != str_list.size())
    {
        cout << "ERROR: str_list size error: " << str_list.size() << " in " << __FUNCTION__
            << endl;

        return;
    }

    int i = 0;

    p_scene_obj->scene_id = str_list[i++].c_str();
    p_scene_obj->obj_type = atoi(str_list[i++].c_str());
    p_scene_obj->obj_id = atoi(str_list[i++].c_str());
    p_scene_obj->obj_res_id = atoi(str_list[i++].c_str());
    p_scene_obj->scene_type = atoi(str_list[i++].c_str());
    p_scene_obj->speed = atoi(str_list[i++].c_str());
    p_scene_obj->x = atoi(str_list[i++].c_str());
    p_scene_obj->y = atoi(str_list[i++].c_str());
    p_scene_obj->is_moving = atoi(str_list[i++].c_str());
    p_scene_obj->last_time = str_list[i++].c_str();
    p_scene_obj->start_x = atoi(str_list[i++].c_str());
    p_scene_obj->start_y = atoi(str_list[i++].c_str());
    p_scene_obj->end_x = atoi(str_list[i++].c_str());
    p_scene_obj->end_y = atoi(str_list[i++].c_str());
	p_scene_obj->arrive_time = str_list[i++].c_str();
	p_scene_obj->move_tolerance = atoi(str_list[i++].c_str());

	CalculatePosition(p_scene_obj);
}

int Scene::CanMove(string scene_id, string dest_scene_id)
{
    return 1;
}

void Scene::CalculatePosition(SceneObj *p_scene_obj)
{
    if(0 == p_scene_obj->is_moving)
    {
        return;
    }

    int mili_sec = Utils::TimeStr2MilliSec(p_scene_obj->arrive_time);
    if(mili_sec <= 0)
    {
        p_scene_obj->is_moving = 0;
        p_scene_obj->x = p_scene_obj->end_x;
        p_scene_obj->y = p_scene_obj->end_y;

        return;
    }

    int distence = mili_sec * p_scene_obj->speed;
    int total_distence = Utils::Distence(p_scene_obj->start_x, p_scene_obj->start_y,
        p_scene_obj->end_x, p_scene_obj->end_y);

    p_scene_obj->x = p_scene_obj->end_x - (int)((p_scene_obj->end_x - p_scene_obj->start_x) *
        ((float)distence / (float)total_distence));
    p_scene_obj->y = p_scene_obj->end_y - (int)((p_scene_obj->end_y - p_scene_obj->start_y) *
        ((float)distence / (float)total_distence));
}
